﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Net;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace project_forgotten_tuba
{
    public class GameClient
    {
        public Container MainContainer { get; set; }

        private NetClient _mainClient;

        public bool IsConnected { get; set; }
        public NetConnection Connection;


        public NetIncomingMessage ReadMessage() { return _mainClient.ReadMessage(); }
        public void Recycle(NetIncomingMessage msg) { _mainClient.Recycle(msg); }

        public void Init()
        {
            int nextport = 14243;
            bool connectworked = false;

            while (!connectworked)
            {
                try
                {
                    _mainClient = new NetClient(GameClientConfig(nextport));
                    _mainClient.Start();
                    connectworked = true;
                }
                catch (Exception e)
                {
                    nextport++;
                }
            }
        }

        public NetPeerConfiguration GameClientConfig(int clientport)
        {
            int _CLIENTPORT_ = clientport;

            NetPeerConfiguration clientConfig = new NetPeerConfiguration("gameConfig"); 
            clientConfig.EnableMessageType(NetIncomingMessageType.WarningMessage);
            clientConfig.EnableMessageType(NetIncomingMessageType.VerboseDebugMessage);
            clientConfig.EnableMessageType(NetIncomingMessageType.ErrorMessage);
            clientConfig.EnableMessageType(NetIncomingMessageType.Error);
            clientConfig.EnableMessageType(NetIncomingMessageType.DebugMessage);
            clientConfig.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
            clientConfig.MaximumTransmissionUnit = 1200;

            clientConfig.PingInterval = 0.5f;
            clientConfig.ConnectionTimeout = 2.0f;
            //clientConfig.SimulatedMinimumLatency = 0.1f;
            //clientConfig.SimulatedRandomLatency = 0.1f;
            //clientConfig.SimulatedLoss = 0.1f;

            clientConfig.Port = _CLIENTPORT_;

            return clientConfig;
        }

        public void Connect(string hostname, int hostport)
        {
            Connection = _mainClient.Connect(hostname, hostport);
        }

        public void HandleAllMessages()
        {
            NetIncomingMessage msg;
            while ((msg = _mainClient.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.StatusChanged:
                        HandleStatusChangedMessage(msg);
                        break;
                    case NetIncomingMessageType.Data:
                        HandleDataMessage(msg);
                        break;
                    case NetIncomingMessageType.ErrorMessage:
                        //LogStatic.Write("Client :: Error Message :: " + msg.ReadString());
                        break;
                    case NetIncomingMessageType.WarningMessage:
                        //LogStatic.Write("Client :: Warning Message :: " + msg.ReadString());
                        break;
                    default:
                        //LogStatic.Write("Client :: Unhandled Message Type :: " + msg.MessageType);
                        break;
                }
                _mainClient.Recycle(msg);
            }
        }

        public void HandleStatusChangedMessage(NetIncomingMessage msg)
        {
            byte statusByte = msg.ReadByte();
            switch (statusByte)
            {
                case (byte)NetConnectionStatus.Disconnected:
                    IsConnected = false;
                    break;
                case (byte)NetConnectionStatus.Connected:
                    IsConnected = true;
                    break;
                default:
                    break;
            }

        }


        public void HandleDataMessage(NetIncomingMessage msg)
        {
            GameMessage.Type msgTypeByte = (GameMessage.Type)msg.ReadByte();
            switch (msgTypeByte)
            {
                case GameMessage.Type.State:
                    HandleDataStateMessage(msg);
                    break;
                case GameMessage.Type.PlayerInput:
                    HandleDataPlayerInputMessage(msg);
                    break;
                case GameMessage.Type.Deck:
                    HandleDeckData(msg);
                    break;
                case GameMessage.Type.DeckRequest:
                    HandleDataDeckRequest(msg);
                    break;
            }
        }


        public void HandleDataStateMessage(NetIncomingMessage msg)
        {
            int numbytes = msg.ReadInt32();
            byte[] compressedstate = msg.ReadBytes(numbytes);
            byte[] decompressedstate = ByteArrayCompressor.Decompress(compressedstate);

            GameState newstate;
            newstate = BinarySerializer.DeserializeFromByteArray<GameState>(decompressedstate);

            newstate.MainContainer = this.MainContainer;

            MainContainer.ClientGameState = newstate;

        }
        public void HandleDataPlayerInputMessage(NetIncomingMessage msg)
        {
            int playerindex = msg.ReadInt32();
            int actiontype = msg.ReadInt32();
            int c = msg.ReadInt32();
            int r = msg.ReadInt32();

            MainContainer.ClientGameState.PerformInput(playerindex, (Container.InputType)actiontype, c, r);
        }
        public void HandleDataDeckRequest(NetIncomingMessage msg)
        {
            int playerindex = MainContainer.ClientPlayerIndex;

            SendDeckConfig(playerindex, MainContainer.ReadLocalDeck());
        }
        public void HandleDeckData(NetIncomingMessage msg)
        {
            int playerindex = msg.ReadInt32();

            int numbytes = msg.ReadInt32();
            byte[] serializeddeck = msg.ReadBytes(numbytes);
            List<string> deck = BinarySerializer.DeserializeFromByteArray<List<string>>(serializeddeck);

            MainContainer.ClientGameState.LoadStringListAsCards(deck, playerindex);
        }

        public void SendInputMessage(int playerindex, Container.InputType inputtype, int c, int r)
        {
            NetOutgoingMessage inputmsg = _mainClient.CreateMessage();
            inputmsg.Write((byte)GameMessage.Type.PlayerInput);
            inputmsg.Write(playerindex);
            inputmsg.Write((int)inputtype);
            inputmsg.Write(c);
            inputmsg.Write(r);
            _mainClient.SendMessage(inputmsg, NetDeliveryMethod.ReliableOrdered);
        }
        public void SendDeckConfig(int playerindex, List<string> deck)
        {
            NetOutgoingMessage deckconfigmessage = _mainClient.CreateMessage();
            deckconfigmessage.Write((byte)GameMessage.Type.Deck);

            deckconfigmessage.Write(playerindex);

            byte[] binaryserializeddeck = BinarySerializer.SerializeToByteArray<List<string>>(deck);
            deckconfigmessage.Write(binaryserializeddeck.Length);
            deckconfigmessage.Write(binaryserializeddeck);

            _mainClient.SendMessage(deckconfigmessage, NetDeliveryMethod.ReliableOrdered);
        }
    }
}
